{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "6d2b5335",
   "metadata": {},
   "source": [
    "<a href=\"https://colab.research.google.com/github/run-llama/llama_index/blob/main/docs/examples/low_level/fusion_retriever.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a0b5c122-d577-4045-b980-cab2eae2aa0c",
   "metadata": {},
   "source": [
    "# Building an Advanced Fusion Retriever from Scratch\n",
    "\n",
    "In this tutorial, we show you how to build an advanced retriever from scratch.\n",
    "\n",
    "Specifically, we show you how to build our `QueryFusionRetriever` from scratch.\n",
    "\n",
    "This is heavily inspired from the RAG-fusion repo here: https://github.com/Raudaschl/rag-fusion."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0d82203e-1aa0-4d85-8a0f-3854dfa81494",
   "metadata": {},
   "source": [
    "## Setup\n",
    "\n",
    "We load documents and build a simple vector index."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9bafe694",
   "metadata": {},
   "outputs": [],
   "source": [
    "%pip install llama-index-readers-file pymupdf\n",
    "%pip install llama-index-llms-openai\n",
    "%pip install llama-index-retrievers-bm25"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c79e8b40-c963-46ee-9601-6c31e5901568",
   "metadata": {},
   "outputs": [],
   "source": [
    "import nest_asyncio\n",
    "\n",
    "nest_asyncio.apply()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "41d6148f-3185-4a32-973c-316f23e45804",
   "metadata": {},
   "source": [
    "#### Load Documents"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c054a492-56c9-4dae-bede-06739858ba57",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "--2024-04-03 09:32:31--  https://arxiv.org/pdf/2307.09288.pdf\n",
      "Resolving arxiv.org (arxiv.org)... 151.101.3.42, 151.101.131.42, 151.101.67.42, ...\n",
      "Connecting to arxiv.org (arxiv.org)|151.101.3.42|:443... connected.\n",
      "HTTP request sent, awaiting response... 200 OK\n",
      "Length: 13661300 (13M) [application/pdf]\n",
      "Saving to: ‘data/llama2.pdf’\n",
      "\n",
      "data/llama2.pdf     100%[===================>]  13.03M  7.44MB/s    in 1.8s    \n",
      "\n",
      "2024-04-03 09:32:33 (7.44 MB/s) - ‘data/llama2.pdf’ saved [13661300/13661300]\n",
      "\n"
     ]
    }
   ],
   "source": [
    "!mkdir data\n",
    "!wget --user-agent \"Mozilla\" \"https://arxiv.org/pdf/2307.09288.pdf\" -O \"data/llama2.pdf\""
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "1126a6d3",
   "metadata": {},
   "source": [
    "If you're opening this Notebook on colab, you will probably need to install LlamaIndex 🦙."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0f03cf99",
   "metadata": {},
   "outputs": [],
   "source": [
    "!pip install llama-index"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b3b7ec9e-30cf-49ba-9b3b-9beb9a2b6758",
   "metadata": {},
   "outputs": [],
   "source": [
    "from pathlib import Path\n",
    "from llama_index.readers.file import PyMuPDFReader\n",
    "\n",
    "loader = PyMuPDFReader()\n",
    "documents = loader.load(file_path=\"./data/llama2.pdf\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "46ea385d",
   "metadata": {},
   "source": [
    "#### Setup Models"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0bc3bd76",
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "\n",
    "os.environ[\"OPENAI_API_KEY\"] = \"sk-...\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "75c07062",
   "metadata": {},
   "outputs": [],
   "source": [
    "from llama_index.llms.openai import OpenAI\n",
    "from llama_index.embeddings.openai import OpenAIEmbedding\n",
    "\n",
    "llm = OpenAI(model=\"gpt-3.5-turbo\", temperature=0.1)\n",
    "embed_model = OpenAIEmbedding(\n",
    "    model=\"text-embedding-3-small\", embed_batch_size=256\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f59a6cd1-802a-4e69-afd1-de6faf4b064b",
   "metadata": {},
   "source": [
    "#### Load into Vector Store"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b2423b4b-5c3b-4d36-b338-9bf74f0e6a82",
   "metadata": {},
   "outputs": [],
   "source": [
    "from llama_index.core import VectorStoreIndex\n",
    "from llama_index.core.node_parser import SentenceSplitter\n",
    "\n",
    "splitter = SentenceSplitter(chunk_size=1024)\n",
    "index = VectorStoreIndex.from_documents(\n",
    "    documents, transformations=[splitter], embed_model=embed_model\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cbc7a9bf-0ef7-45a5-bc76-3c37a8a09c88",
   "metadata": {},
   "source": [
    "## Define Advanced Retriever\n",
    "\n",
    "We define an advanced retriever that performs the following steps:\n",
    "1. Query generation/rewriting: generate multiple queries given the original user query\n",
    "2. Perform retrieval for each query over an ensemble of retrievers.\n",
    "3. Reranking/fusion: fuse results from all queries, and apply a reranking step to \"fuse\" the top relevant results!\n",
    "\n",
    "Then in the next section we'll plug this into our response synthesis module."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3586a793-3c5a-4d7c-b401-cd6fb71f87a1",
   "metadata": {},
   "source": [
    "### Step 1: Query Generation/Rewriting\n",
    "\n",
    "The first step is to generate queries from the original query to better match the query intent, and increase precision/recall of the retrieved results. For instance, we might be able to rewrite the query into smaller queries.\n",
    "\n",
    "We can do this by prompting ChatGPT."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0a5183a0-58ce-4cc5-a74b-8428dfe12bb5",
   "metadata": {},
   "outputs": [],
   "source": [
    "from llama_index.core import PromptTemplate"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9e745f03-5c06-43b5-ad9d-65c5b8150ba7",
   "metadata": {},
   "outputs": [],
   "source": [
    "query_str = \"How do the models developed in this work compare to open-source chat models based on the benchmarks tested?\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0a17441d-1f14-4de4-a4b3-55177d0a2dee",
   "metadata": {},
   "outputs": [],
   "source": [
    "query_gen_prompt_str = (\n",
    "    \"You are a helpful assistant that generates multiple search queries based on a \"\n",
    "    \"single input query. Generate {num_queries} search queries, one on each line, \"\n",
    "    \"related to the following input query:\\n\"\n",
    "    \"Query: {query}\\n\"\n",
    "    \"Queries:\\n\"\n",
    ")\n",
    "query_gen_prompt = PromptTemplate(query_gen_prompt_str)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5c3a7b04-c4fb-456c-8584-5ea93bdc7bf0",
   "metadata": {},
   "outputs": [],
   "source": [
    "def generate_queries(llm, query_str: str, num_queries: int = 4):\n",
    "    fmt_prompt = query_gen_prompt.format(\n",
    "        num_queries=num_queries - 1, query=query_str\n",
    "    )\n",
    "    response = llm.complete(fmt_prompt)\n",
    "    queries = response.text.split(\"\\n\")\n",
    "    return queries"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2a577a95-2b58-424d-aa7d-bed9aa9ceb98",
   "metadata": {},
   "outputs": [],
   "source": [
    "queries = generate_queries(llm, query_str, num_queries=4)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "73fe53d2-c556-44ed-a255-374ae8eca494",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['1. Comparison of models developed in this work to open-source chat models in benchmark testing', '2. Performance evaluation of models developed in this work versus open-source chat models on tested benchmarks', '3. Analysis of differences between models developed in this work and open-source chat models in benchmark assessments']\n"
     ]
    }
   ],
   "source": [
    "print(queries)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a9876c65-1a90-4606-9104-5df10009d935",
   "metadata": {},
   "source": [
    "### Step 2: Perform Vector Search for Each Query\n",
    "\n",
    "Now we run retrieval for each query. This means that we fetch the top-k most relevant results from each vector store.\n",
    "\n",
    "**NOTE**: We can also have multiple retrievers. Then the total number of queries we run is N*M, where N is number of retrievers and M is number of generated queries. Hence there will also be N*M retrieved lists.\n",
    "\n",
    "Here we'll use the retriever provided from our vector store. If you want to see how to build this from scratch please see [our tutorial on this](https://docs.llamaindex.ai/en/latest/examples/low_level/retrieval.html#put-this-into-a-retriever)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "53114651-0b57-4cbc-a07f-d906c5820cb7",
   "metadata": {},
   "outputs": [],
   "source": [
    "from tqdm.asyncio import tqdm\n",
    "\n",
    "\n",
    "async def run_queries(queries, retrievers):\n",
    "    \"\"\"Run queries against retrievers.\"\"\"\n",
    "    tasks = []\n",
    "    for query in queries:\n",
    "        for i, retriever in enumerate(retrievers):\n",
    "            tasks.append(retriever.aretrieve(query))\n",
    "\n",
    "    task_results = await tqdm.gather(*tasks)\n",
    "\n",
    "    results_dict = {}\n",
    "    for i, (query, query_result) in enumerate(zip(queries, task_results)):\n",
    "        results_dict[(query, i)] = query_result\n",
    "\n",
    "    return results_dict"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d046d284-ab9e-4242-b91a-8d06c472dfaf",
   "metadata": {},
   "outputs": [],
   "source": [
    "# get retrievers\n",
    "from llama_index.retrievers.bm25 import BM25Retriever\n",
    "\n",
    "\n",
    "## vector retriever\n",
    "vector_retriever = index.as_retriever(similarity_top_k=2)\n",
    "\n",
    "## bm25 retriever\n",
    "bm25_retriever = BM25Retriever.from_defaults(\n",
    "    docstore=index.docstore, similarity_top_k=2\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6a0ddc59-1602-4078-b3e9-dadb852709fc",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "  0%|          | 0/6 [00:00<?, ?it/s]"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 6/6 [00:00<00:00, 11.14it/s]\n"
     ]
    }
   ],
   "source": [
    "results_dict = await run_queries(queries, [vector_retriever, bm25_retriever])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "65f13dff-e5fa-4c60-865c-7217dd4c87e6",
   "metadata": {},
   "source": [
    "### Step 3: Perform Fusion\n",
    "\n",
    "The next step here is to perform fusion: combining the results from several retrievers into one and re-ranking.\n",
    "\n",
    "Note that a given node might be retrieved multiple times from different retrievers, so there needs to be a way to de-dup and rerank the node given the multiple retrievals.\n",
    "\n",
    "We'll show you how to perform \"reciprocal rank fusion\": for each node, add up its reciprocal rank in every list where it's retrieved.\n",
    "\n",
    "Then reorder nodes by highest score to least.\n",
    "\n",
    "Full paper here: https://plg.uwaterloo.ca/~gvcormac/cormacksigir09-rrf.pdf"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a4702259-ea82-4e08-b64c-1300a3857c63",
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "from llama_index.core.schema import NodeWithScore\n",
    "\n",
    "\n",
    "def fuse_results(results_dict, similarity_top_k: int = 2):\n",
    "    \"\"\"Fuse results.\"\"\"\n",
    "    k = 60.0  # `k` is a parameter used to control the impact of outlier rankings.\n",
    "    fused_scores = {}\n",
    "    text_to_node = {}\n",
    "\n",
    "    # compute reciprocal rank scores\n",
    "    for nodes_with_scores in results_dict.values():\n",
    "        for rank, node_with_score in enumerate(\n",
    "            sorted(\n",
    "                nodes_with_scores, key=lambda x: x.score or 0.0, reverse=True\n",
    "            )\n",
    "        ):\n",
    "            text = node_with_score.node.get_content()\n",
    "            text_to_node[text] = node_with_score\n",
    "            if text not in fused_scores:\n",
    "                fused_scores[text] = 0.0\n",
    "            fused_scores[text] += 1.0 / (rank + k)\n",
    "\n",
    "    # sort results\n",
    "    reranked_results = dict(\n",
    "        sorted(fused_scores.items(), key=lambda x: x[1], reverse=True)\n",
    "    )\n",
    "\n",
    "    # adjust node scores\n",
    "    reranked_nodes: List[NodeWithScore] = []\n",
    "    for text, score in reranked_results.items():\n",
    "        reranked_nodes.append(text_to_node[text])\n",
    "        reranked_nodes[-1].score = score\n",
    "\n",
    "    return reranked_nodes[:similarity_top_k]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "497cf22e-a5fe-4dcd-9227-13cf63b3622c",
   "metadata": {},
   "outputs": [],
   "source": [
    "final_results = fuse_results(results_dict)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "87061aed-fbfb-4949-9fc6-f687050314d0",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.03333333333333333 \n",
      " Figure 12: Human evaluation results for Llama 2-Chat models compared to open- and closed-source models\n",
      "across ~4,000 helpfulness prompts with three raters per prompt.\n",
      "The largest Llama 2-Chat model is competitive with ChatGPT. Llama 2-Chat 70B model has a win rate of\n",
      "36% and a tie rate of 31.5% relative to ChatGPT. Llama 2-Chat 70B model outperforms PaLM-bison chat\n",
      "model by a large percentage on our prompt set. More results and analysis is available in Section A.3.7.\n",
      "Inter-Rater Reliability (IRR).\n",
      "In our human evaluations, three different annotators provided independent\n",
      "assessments for each model generation comparison. High IRR scores (closer to 1.0) are typically seen as\n",
      "better from a data quality perspective, however, context is important. Highly subjective tasks like evaluating\n",
      "the overall helpfulness of LLM generations will usually have lower IRR scores than more objective labelling\n",
      "tasks. There are relatively few public benchmarks for these contexts, so we feel sharing our analysis here will\n",
      "benefit the research community.\n",
      "We used Gwet’s AC1/2 statistic (Gwet, 2008, 2014) to measure inter-rater reliability (IRR), as we found it to\n",
      "be the most stable metric across different measurement scenarios. On the 7-point Likert scale helpfulness\n",
      "task that is used in our analysis, Gwet’s AC2 score varies between 0.37 and 0.55 depending on the specific\n",
      "model comparison. We see scores on the lower end of that range for ratings from model comparisons with\n",
      "similar win rates to each other (like the Llama 2-Chat-70B-chat vs. ChatGPT comparison). We see scores on\n",
      "the higher end of that range for ratings from model comparisons with a more clear winner (like the Llama\n",
      "2-Chat-34b-chat vs. Falcon-40b-instruct).\n",
      "Limitations of human evaluations.\n",
      "While our results indicate that Llama 2-Chat is on par with ChatGPT\n",
      "on human evaluations, it is important to note that human evaluations have several limitations.\n",
      "• By academic and research standards, we have a large prompt set of 4k prompts. However, it does not cover\n",
      "real-world usage of these models, which will likely cover a significantly larger number of use cases.\n",
      "• Diversity of the prompts could be another factor in our results. For example, our prompt set does not\n",
      "include any coding- or reasoning-related prompts.\n",
      "• We only evaluate the final generation of a multi-turn conversation. A more interesting evaluation could be\n",
      "to ask the models to complete a task and rate the overall experience with the model over multiple turns.\n",
      "• Human evaluation for generative models is inherently subjective and noisy. As a result, evaluation on a\n",
      "different set of prompts or with different instructions could result in different results.\n",
      "19 \n",
      "********\n",
      "\n",
      "0.03306010928961749 \n",
      " Llama 2: Open Foundation and Fine-Tuned Chat Models\n",
      "Hugo Touvron∗\n",
      "Louis Martin†\n",
      "Kevin Stone†\n",
      "Peter Albert Amjad Almahairi Yasmine Babaei Nikolay Bashlykov Soumya Batra\n",
      "Prajjwal Bhargava Shruti Bhosale Dan Bikel Lukas Blecher Cristian Canton Ferrer Moya Chen\n",
      "Guillem Cucurull David Esiobu Jude Fernandes Jeremy Fu Wenyin Fu Brian Fuller\n",
      "Cynthia Gao Vedanuj Goswami Naman Goyal Anthony Hartshorn Saghar Hosseini Rui Hou\n",
      "Hakan Inan Marcin Kardas Viktor Kerkez Madian Khabsa Isabel Kloumann Artem Korenev\n",
      "Punit Singh Koura Marie-Anne Lachaux Thibaut Lavril Jenya Lee Diana Liskovich\n",
      "Yinghai Lu Yuning Mao Xavier Martinet Todor Mihaylov Pushkar Mishra\n",
      "Igor Molybog Yixin Nie Andrew Poulton Jeremy Reizenstein Rashi Rungta Kalyan Saladi\n",
      "Alan Schelten Ruan Silva Eric Michael Smith Ranjan Subramanian Xiaoqing Ellen Tan Binh Tang\n",
      "Ross Taylor Adina Williams Jian Xiang Kuan Puxin Xu Zheng Yan Iliyan Zarov Yuchen Zhang\n",
      "Angela Fan Melanie Kambadur Sharan Narang Aurelien Rodriguez Robert Stojnic\n",
      "Sergey Edunov\n",
      "Thomas Scialom∗\n",
      "GenAI, Meta\n",
      "Abstract\n",
      "In this work, we develop and release Llama 2, a collection of pretrained and fine-tuned\n",
      "large language models (LLMs) ranging in scale from 7 billion to 70 billion parameters.\n",
      "Our fine-tuned LLMs, called Llama 2-Chat, are optimized for dialogue use cases. Our\n",
      "models outperform open-source chat models on most benchmarks we tested, and based on\n",
      "our human evaluations for helpfulness and safety, may be a suitable substitute for closed-\n",
      "source models. We provide a detailed description of our approach to fine-tuning and safety\n",
      "improvements of Llama 2-Chat in order to enable the community to build on our work and\n",
      "contribute to the responsible development of LLMs.\n",
      "∗Equal contribution, corresponding authors: {tscialom, htouvron}@meta.com\n",
      "†Second author\n",
      "Contributions for all the authors can be found in Section A.1.\n",
      "arXiv:2307.09288v2  [cs.CL]  19 Jul 2023 \n",
      "********\n",
      "\n"
     ]
    }
   ],
   "source": [
    "for n in final_results:\n",
    "    print(n.score, \"\\n\", n.text, \"\\n********\\n\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8aa602b1-4188-48ae-9d6c-127778019261",
   "metadata": {},
   "source": [
    "**Analysis**: The above code has a few straightforward components.\n",
    "1. Go through each node in each retrieved list, and add it's reciprocal rank to the node's ID. The node's ID is the hash of it's text for dedup purposes.\n",
    "2. Sort results by highest-score to lowest.\n",
    "3. Adjust node scores."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5d92ae5d-1220-4988-998e-06a22cc4f7b2",
   "metadata": {},
   "source": [
    "## Plug into RetrieverQueryEngine\n",
    "\n",
    "Now we're ready to define this as a custom retriever, and plug it into our `RetrieverQueryEngine` (which does retrieval and synthesis)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a4ccd8d7-9fc5-463c-ac0c-f9e1d1bae63c",
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "\n",
    "from llama_index.core import QueryBundle\n",
    "from llama_index.core.retrievers import BaseRetriever\n",
    "from llama_index.core.schema import NodeWithScore\n",
    "import asyncio\n",
    "\n",
    "\n",
    "class FusionRetriever(BaseRetriever):\n",
    "    \"\"\"Ensemble retriever with fusion.\"\"\"\n",
    "\n",
    "    def __init__(\n",
    "        self,\n",
    "        llm,\n",
    "        retrievers: List[BaseRetriever],\n",
    "        similarity_top_k: int = 2,\n",
    "    ) -> None:\n",
    "        \"\"\"Init params.\"\"\"\n",
    "        self._retrievers = retrievers\n",
    "        self._similarity_top_k = similarity_top_k\n",
    "        self._llm = llm\n",
    "        super().__init__()\n",
    "\n",
    "    def _retrieve(self, query_bundle: QueryBundle) -> List[NodeWithScore]:\n",
    "        \"\"\"Retrieve.\"\"\"\n",
    "        queries = generate_queries(\n",
    "            self._llm, query_bundle.query_str, num_queries=4\n",
    "        )\n",
    "        results = asyncio.run(run_queries(queries, self._retrievers))\n",
    "        final_results = fuse_results(\n",
    "            results, similarity_top_k=self._similarity_top_k\n",
    "        )\n",
    "\n",
    "        return final_results"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b2b641b1-e64e-4ddf-9cc5-88ff5c57b70e",
   "metadata": {},
   "outputs": [],
   "source": [
    "from llama_index.core.query_engine import RetrieverQueryEngine\n",
    "\n",
    "fusion_retriever = FusionRetriever(\n",
    "    llm, [vector_retriever, bm25_retriever], similarity_top_k=2\n",
    ")\n",
    "\n",
    "query_engine = RetrieverQueryEngine(fusion_retriever)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c0d81b5b-39f2-42da-92b9-ce6113fa43d9",
   "metadata": {},
   "outputs": [],
   "source": [
    "response = query_engine.query(query_str)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "93daeaaa-ce68-465a-b246-287714b4b370",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The models developed in this work, specifically the Llama 2-Chat models, outperform open-source chat models on most benchmarks that were tested.\n"
     ]
    }
   ],
   "source": [
    "print(str(response))"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
